package me.tonywang.quartz;

import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.scheduling.SchedulingException;
import org.springframework.util.Assert;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @className: QuartzUtils
 * @description:
 * @author: WANGHUI
 * @createDate: 2018/5/15 11:48
 * @version: 1.0
 */
public final class QuartzUtils {

    /**
     * 添加任务到quartz或者修改启动表达式
     *
     * @param scheduler
     * @param trigger
     * @return
     * @throws SchedulerException
     */
    public static boolean addTriggerToScheduler(Scheduler scheduler, Trigger trigger) throws SchedulerException {
        boolean triggerExists = (scheduler.getTrigger(trigger.getKey()) != null);
        // Check if the Trigger is aware of an associated JobDetail.
        JobDetail jobDetail = (JobDetail) trigger.getJobDataMap().remove("jobDetail");
        if (triggerExists) {
            scheduler.rescheduleJob(trigger.getKey(), trigger);
        } else {
            try {
                if (jobDetail != null) {
                    scheduler.scheduleJob(jobDetail, trigger);
                } else {
                    scheduler.scheduleJob(trigger);
                }
            } catch (ObjectAlreadyExistsException ex) {
                scheduler.rescheduleJob(trigger.getKey(), trigger);
            }
        }
        return true;
    }


    /**
     * 更新job内容
     *
     * @param scheduler
     * @param trigger
     * @return
     * @throws SchedulerException
     */
    public static boolean updateTriggerToScheduler(Scheduler scheduler, Trigger trigger) throws SchedulerException {
        QuartzUtils.deleteJob(scheduler, trigger.getJobKey());
        return QuartzUtils.addTriggerToScheduler(scheduler, trigger);
    }


    /**
     * 立即执行Job
     *
     * @param scheduler
     * @param jobKey
     * @throws SchedulerException
     */
    public static void triggerJob(Scheduler scheduler, JobKey jobKey) throws SchedulerException {
        scheduler.triggerJob(jobKey);
    }

    /**
     * 立即执行临时任务Job
     *
     * @param scheduler
     * @param jobDetail
     * @throws SchedulerException
     */
    public static void triggerTempJob(Scheduler scheduler, JobDetail jobDetail,int startupDelay) throws SchedulerException {

        scheduler.addJob(jobDetail, false, true);

        // Not using the Quartz startDelayed method since we explicitly want a daemon
			// thread here, not keeping the JVM alive in case of all other threads ending.
        final JobKey jobKey = jobDetail.getKey();
        Thread schedulerThread = new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(TimeUnit.SECONDS.toMillis(startupDelay));
                }
                catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                    // simply proceed
                }
                try {
                     scheduler.triggerJob(jobKey);
                }
                catch (SchedulerException ex) {
                    throw new SchedulingException("Could not triggerJob  " + jobKey, ex);
                }
            }
        };
        schedulerThread.setName("Quartz Scheduler [" + scheduler.getSchedulerName() + "] triggerJob "+ jobKey);
        schedulerThread.setDaemon(true);
        schedulerThread.start();
    }

    /**
     * 立即执行临时任务Job
     *
     * @param scheduler
     * @param jobDetail
     * @throws SchedulerException
     */
    public static void triggerTempJob(Scheduler scheduler, JobDetail jobDetail) throws SchedulerException {
        scheduler.addJob(jobDetail, false, true);
        scheduler.triggerJob(jobDetail.getKey());
    }


    /**
     * 获取job的状态
     * @param scheduler
     * @param jobKey
     * @return
     * @throws SchedulerException
     */
    public static String getJobTriggerState(Scheduler scheduler, JobKey jobKey) throws SchedulerException {
         TriggerKey triggerKey =  new TriggerKey(jobKey.getName(),jobKey.getGroup());
         Assert.state(scheduler.checkExists(triggerKey), triggerKey + " not exist in scheduler");
         return scheduler.getTriggerState(triggerKey).toString();
    }


    /**
     * 删除任务
     *
     * @param scheduler
     * @param jobKey
     * @return
     * @throws SchedulerException
     */
    public static void deleteJob(Scheduler scheduler, JobKey jobKey) throws SchedulerException {
        Assert.state(scheduler.checkExists(jobKey), jobKey + " not exist in scheduler");
        scheduler.deleteJob(jobKey);
    }

    /**
     * 删除指定的JOB
     *
     * @param scheduler
     * @param jobKeys
     * @throws SchedulerException
     */
    public static void deleteJobs(Scheduler scheduler, List<JobKey> jobKeys) throws SchedulerException {
        for (JobKey jobKey : jobKeys) {
            deleteJob(scheduler, jobKey);
        }
    }

    /**
     * 删除组内任务
     *
     * @param scheduler
     * @param jobGroup
     * @return
     * @throws SchedulerException
     */
    public static void deleteJobs(Scheduler scheduler, String jobGroup) throws SchedulerException {
        Set<JobKey> jobKeys = scheduler.getJobKeys(GroupMatcher.groupEquals(jobGroup));
        for (JobKey jobKey : jobKeys) {
           deleteJob(scheduler, jobKey);
        }
    }




    /**
     * 暂停任务
     *
     * @param scheduler
     * @param jobKey
     * @throws SchedulerException
     */
    public static void pauseJob(Scheduler scheduler, JobKey jobKey) throws SchedulerException {
        Assert.state(scheduler.checkExists(jobKey), jobKey + " not exist in scheduler");
        scheduler.pauseJob(jobKey);
    }

    /**
     * 暂停groupName 组内任务，处于暂停状态的组；如果有新增Job，也会被暂停
     * <p>
     * if there are pre-existing groups "aaa" and
     * * "bbb" and a matcher is given to pause the group "axx" (with a
     * * group equals matcher) then no jobs will be paused, but it will be
     * * remembered that group "axx" is paused and later when a job is added
     * * in that group, it will become paused.
     * </p>
     *
     * @param scheduler
     * @param groupName
     * @throws SchedulerException
     */
    public static void pauseJobs(Scheduler scheduler, String groupName) throws SchedulerException {
        if (!scheduler.isInStandbyMode())
            scheduler.pauseJobs(GroupMatcher.groupEquals(groupName));
        Set<JobKey> jobKeys = scheduler.getJobKeys(GroupMatcher.groupEquals(groupName));
        for (JobKey jobKey : jobKeys) {
            scheduler.pauseJob(jobKey);
        }
    }

    /**
     * 暂停所有任务
     *
     * @param scheduler
     * @throws SchedulerException
     */
    public static void pauseAll(Scheduler scheduler) throws SchedulerException {
        if (!scheduler.isInStandbyMode()) scheduler.pauseAll();
    }

    /**
     * 恢复任务
     *
     * @param scheduler
     * @param jobKey
     * @throws SchedulerException
     */
    public static void resumeJob(Scheduler scheduler, JobKey jobKey) throws SchedulerException {
         Assert.state(scheduler.checkExists(jobKey), jobKey + " not exist in scheduler");
         scheduler.resumeJob(jobKey);

    }

    /**
     * 恢复groupName的所有任务
     *
     * @param scheduler
     * @param groupName
     * @throws SchedulerException
     */
    public static void resumeJobs(Scheduler scheduler, String groupName) throws SchedulerException {
        if (!scheduler.isInStandbyMode()) scheduler.resumeJobs(GroupMatcher.groupEquals(groupName));
        Set<JobKey> jobKeys = scheduler.getJobKeys(GroupMatcher.groupEquals(groupName));
        for (JobKey jobKey : jobKeys) {
            scheduler.resumeJob(jobKey);
        }
    }


    /**
     * 恢复所有任务
     *
     * @param scheduler
     * @throws SchedulerException
     */
    public static void resumeAll(Scheduler scheduler) throws SchedulerException {
        if (!scheduler.isInStandbyMode()) scheduler.resumeAll();
    }


    /**
     * 启动
     *
     * @param scheduler
     * @throws SchedulerException
     */
    public static void start(Scheduler scheduler) throws SchedulerException {
        if (!scheduler.isStarted()) scheduler.start();
    }

    /**
     * 延迟启动
     *
     * @param scheduler
     * @param seconds   延迟时间
     * @throws SchedulerException
     */
    public static void startDelayed(Scheduler scheduler, int seconds) throws SchedulerException {
        if (!scheduler.isStarted()) scheduler.startDelayed(seconds);
    }


    /**
     * Temporarily halts the <code>Scheduler</code>'s firing of <code>{@link Trigger}s</code>.
     *
     * <p>
     * When <code>start()</code> is called (to bring the scheduler out of
     * stand-by mode), trigger misfire instructions will NOT be applied
     * during the execution of the <code>start()</code> method - any misfires
     * will be detected immediately afterward (by the <code>JobStore</code>'s
     * normal process).
     * </p>
     *
     * <p>
     * The scheduler is not destroyed, and can be re-started at any time.
     * </p>
     *
     * @see #start(Scheduler scheduler)
     * @see #pauseAll(Scheduler scheduler)
     * dangerous OPERATION ,USE PAUSEALL
     */
    public static void standby(Scheduler scheduler) throws SchedulerException {
        if (!scheduler.isInStandbyMode()) scheduler.standby();
    }

    /**
     * 验证cronExpression是否正确
     *
     * @param cronExpression
     * @return
     */
    public static boolean isValidExpression(String cronExpression) {
        return CronExpression.isValidExpression(cronExpression);
    }

    /**
     * 验证cronExpression是否正确
     *
     * @param cronExpression
     * @return
     */
    public static void validateExpression(String cronExpression) throws ParseException {
        new CronExpression(cronExpression);
    }

    /**
     * 当前quartz节点是否在集群环境中，生产环境多应用节点必须在集群中，避免重复执行
     *
     * @param scheduler
     * @return
     * @throws SchedulerException
     */
    public static boolean isJobStoreClustered(Scheduler scheduler) throws SchedulerException {
        return getMetaData(scheduler).isJobStoreClustered();
    }

    /**
     * 获取当前quartz节点的快照信息，注意生产环境下不能反应所有quartz节点的情况，生产环境下使用QRTZ_*表记录
     *
     * @param scheduler
     * @return
     * @throws SchedulerException
     */
    public static SchedulerMetaData getMetaData(Scheduler scheduler) throws SchedulerException {
        return scheduler.getMetaData();
    }


      /**
     * 获取当前quartz节点的快照信息，注意生产环境下不能反应所有quartz节点的情况，生产环境下使用QRTZ_*表记录
     *
     * @param scheduler
     * @return
     * @throws SchedulerException
     */
    public static List<JobExecutionContext> getCurrentlyExecutingJobs(Scheduler scheduler) throws SchedulerException {
        return scheduler.getCurrentlyExecutingJobs();
    }
}
